package wwl.lsf.hellospringboot.baidu;

import org.apache.commons.lang.StringUtils;
import org.apache.http.*;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * http 工具类
 */
public class HttpUtil {

    public static String post(String requestUrl, String accessToken, String params)
            throws Exception {
        String contentType = "application/x-www-form-urlencoded";
        return HttpUtil.post(requestUrl, accessToken, contentType, params);
    }

    public static String post(String requestUrl, String accessToken, String contentType, String params)
            throws Exception {
        String encoding = "UTF-8";
        if (requestUrl.contains("nlp")) {
            encoding = "GBK";
        }
        return HttpUtil.post(requestUrl, accessToken, contentType, params, encoding);
    }

    public static String post(String requestUrl, String accessToken, String contentType, String params, String encoding)
            throws Exception {
        String url = requestUrl + "?access_token=" + accessToken;
        return HttpUtil.GeneralUrl(url, contentType, params, encoding, "POST");
    }

    public static String get(String requestUrl)
            throws Exception {
        return HttpUtil.GeneralUrl(requestUrl, "text/html;charset=UTF-8", "", "UTF-8", "GET");
    }

    public static String GeneralUrl(String generalUrl, String contentType, String params, String encoding, String requestMethod)
            throws Exception {
        URL url = new URL(generalUrl);
        // 打开和URL之间的连接
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        if (StringUtils.isNotEmpty(requestMethod)) {
            connection.setRequestMethod(requestMethod);

        }
        // 设置通用的请求属性
        connection.setRequestProperty("Content-Type", contentType);
        connection.setRequestProperty("Connection", "Keep-Alive");
        connection.setRequestProperty("Cookie", "Cookie_3=value; Hm_ck_1679906639550=42; Hm_ck_1679906676839=42; Hm_ck_1679907553925=42; Hm_ck_1679965847344=42; Hm_ck_1679965873224=42; Hm_lpvt_b35ba8c1e96fa5437eb8e18694422ca5=1679965873; Hm_lvt_b35ba8c1e96fa5437eb8e18694422ca5=1679537133; JSESSIONID=0DCC28C4A47559DAB3B4F5643356AE8F");
        connection.setUseCaches(false);
        connection.setDoOutput(true);
        connection.setDoInput(true);

        // 得到请求的输出流对象
        DataOutputStream out = new DataOutputStream(connection.getOutputStream());
        out.write(params.getBytes(encoding));
        out.flush();
        out.close();

        // 建立实际的连接
        connection.connect();
        // 获取所有响应头字段
        Map<String, List<String>> headers = connection.getHeaderFields();
        // 遍历所有的响应头字段
        for (String key : headers.keySet()) {
            System.err.println(key + "--->" + headers.get(key));
        }
        // 定义 BufferedReader输入流来读取URL的响应
        BufferedReader in = null;
        in = new BufferedReader(
                new InputStreamReader(connection.getInputStream(), encoding));
        String result = "";
        String getLine;
        while ((getLine = in.readLine()) != null) {
            result += getLine;
        }
        in.close();
        System.err.println("result:" + result);
        return result;
    }

    public static void main(String[] args) throws Exception {
        Map<String,String> headerMap  = new HashMap<>();
        headerMap.put("Cookie", "Cookie_3=value; Hm_ck_1679906639550=42; Hm_ck_1679906676839=42; Hm_ck_1679907553925=42; Hm_ck_1679965847344=42; Hm_ck_1679965873224=42; Hm_lpvt_b35ba8c1e96fa5437eb8e18694422ca5=1679965873; Hm_lvt_b35ba8c1e96fa5437eb8e18694422ca5=1679537133; JSESSIONID=0DCC28C4A47559DAB3B4F5643356AE8F");
        String s = HttpUtil.httpGet("http://eip.hengchang6.com/api/portal/compo/attendance/composite_info?month=2023-03&employeeId=10410013",headerMap);
        System.out.println("s = " + s);
    }

    public static String httpGet(String url, Map<String,String> headerMap) throws Exception{
        String result = "";
        CloseableHttpResponse resp = null;
        try{
            CloseableHttpClient client = getHttpClient(url);
            HttpGet httpGet = new HttpGet(url);
            config(httpGet);
            if(headerMap != null && !headerMap.isEmpty()){
                for(String name:headerMap.keySet()){
                    httpGet.addHeader(name,headerMap.get(name));
                }
            }
            resp = client.execute(httpGet);
            if(resp.getStatusLine().getStatusCode() == 200) {
                HttpEntity he = resp.getEntity();
                result = EntityUtils.toString(he);
            }else {
                handlerHttpStatusCode(resp.getStatusLine().getStatusCode());
            }
        }catch(Exception e){
            throw e;
        }finally {
            try {
                if(resp!=null) {
                    resp.close();
                }
            } catch (IOException e) {
                throw e;
            }
        }
        return result;
    }
    private static void config(HttpRequestBase httpRequestBase) {
        // 配置请求的超时设置
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(connTimeout)
                .setConnectTimeout(connTimeout).setSocketTimeout(socketTimeout).build();
        httpRequestBase.setConfig(requestConfig);
    }
    private static final int connTimeout = 60 * 1000;
    /**
     * 等待响应时间
     */
    private static final int socketTimeout = 60 * 1000;
    private static void handlerHttpStatusCode(int statusCode) throws Exception{
        String httpCode = String.valueOf(statusCode);
        System.out.println("httpCode = " + httpCode);
    }
    private static CloseableHttpClient httpClient = null;

    private final static Object syncLock = new Object();
    private static CloseableHttpClient getHttpClient(String url) {
        String hostname = url.startsWith("http")?url.split("/")[2]:url.split("/")[0];
        int port = 80;
        if (hostname.contains(":")) {
            String[] arr = hostname.split(":");
            hostname = arr[0];
            port = Integer.parseInt(arr[1]);
        }
        if (httpClient == null) {
            synchronized (syncLock) {
                if (httpClient == null) {
                    httpClient = createHttpClient(200, 40, 100, hostname, port);
                }
            }
        }
        return httpClient;
    }
    private static CloseableHttpClient createHttpClient(int maxTotal, int maxPerRoute, int maxRoute, String hostname, int port) {
        ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
        LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory.getSocketFactory();
        Registry<ConnectionSocketFactory> registry = RegistryBuilder
                .<ConnectionSocketFactory> create().register("http", plainsf)
                .register("https", sslsf).build();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
        // 将最大连接数增加
        cm.setMaxTotal(maxTotal);
        // 将每个路由基础的连接增加
        cm.setDefaultMaxPerRoute(maxPerRoute);
        HttpHost httpHost = new HttpHost(hostname, port);
        // 将目标主机的最大连接数增加
        cm.setMaxPerRoute(new HttpRoute(httpHost), maxRoute);
        // 请求重试处理
        HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {
            /**
             * 是否重试
             * @param exception
             * @param executionCount
             * @param context
             * @return
             */
            @Override
            public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
                if (executionCount >= 3) {// 如果已经重试了5次，就放弃
                    return false;
                }
                if (exception instanceof NoHttpResponseException) {// 如果服务器丢掉了连接，那么就重试
                    return true;
                }
                if (exception instanceof SSLHandshakeException) {// 不要重试SSL握手异常
                    return false;
                }
                if (exception instanceof InterruptedIOException) {// 超时
                    return false;
                }
                if (exception instanceof UnknownHostException) {// 目标服务器不可达
                    return false;
                }
                if (exception instanceof ConnectTimeoutException) {// 连接被拒绝
                    return false;
                }
                if (exception instanceof SSLException) {// SSL握手异常
                    return false;
                }

                HttpClientContext clientContext = HttpClientContext.adapt(context);
                HttpRequest request = clientContext.getRequest();
                // 如果请求是幂等的，就再次尝试
                if (!(request instanceof HttpEntityEnclosingRequest)) {
                    return true;
                }
                return false;
            }
        };

        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm)
                .setRetryHandler(httpRequestRetryHandler).build();
        return httpClient;
    }
}
